<?php

namespace common\logics\trade;

use Yii;
use Throwable;
use yii\base\Exception;
use common\models\shop\ShopOrder;
use common\models\shop\ShopOrderUnified;
use common\models\trade\Trade;
use common\models\trade\TradePartner;
use common\models\trade\TradeOrder;
use common\models\shop\ShopOrderRefund;
use common\models\shop\ShopOrderRefundLog;
use common\models\shop\ShopOrderServiceApply;
use common\models\shop\ShopServiceSupportWorkflow;
use Alipay\aop\request\alipay\TradeRequest;
use Alipay\aop\AopClient;
use common\models\user\UserAccount;
use common\models\user\UserAccountLog;
use common\components\wechat\PaymentWechat;
use common\helpers\MathHelper;

/**
 * 交易退款操作模型
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class RefundTradeForm extends \common\base\Model {

    /**
     * @var string 支付范围
     */
    const SCOPE_SHOP_REFUND = 'shop_refund';

    /**
     * @var ShopOrderServiceApply 
     */
    public $apply;

    /**
     * @var ShopOrderRefund 
     */
    public $refund;

    /**
     * @var ShopServiceSupportWorkflow 
     */
    public $workflow;
    private $_canRefundCreditFees = 0;
    private $_canRefundCashFees = 0;
    private $_canRefundAmountFees = 0;

    /**
     * @var TradePartner 授权应用
     */
    private $_partner = null;

    /**
     * {@inheritdoc}
     */
    public function rules() {
        return [
            [['refund', 'workflow'], 'required'],
            ['refund', 'validateRefund'],
            ['apply', 'safe'],
        ];
    }

    /**
     * This validate Refund attribute value.
     * 
     * @param string $attribute
     * @return mixed
     */
    public function validateRefund($attribute) {
        if ($this->hasErrors()) {
            return;
        }
        /* @var $refund ShopOrderRefund */
        $refund = $this->$attribute;
        if ($refund->isSuccess) {
            return $this->addError($attribute, '已退款');
        }
        /* @var $unifiedTrades Trade[] */
        $unifiedTrades = $refund->unifiedTrade;
        $canRefundAmountFees = 0;
        $canRefundCreditFees = 0;
        $canRefundCashFees = 0;
        foreach ($unifiedTrades as $trade) {
            if (!$trade->isPayment) {
                continue;
            }
            $canRefundAmountFees += $trade->balanceAmountFee;
            $canRefundCreditFees += $trade->balanceCreditFee;
            $canRefundCashFees += $trade->balanceCashFee;
        }
        $applyRefundAmountFees = intval($refund->amount * 100);
        if ($applyRefundAmountFees > $canRefundAmountFees) {
            return $this->addError($attribute, '退款金额不符合条件');
        }
        $this->_canRefundCreditFees = $canRefundCreditFees;
        $this->_canRefundCashFees = $canRefundCashFees;
        $this->_canRefundAmountFees = $canRefundAmountFees;
        //
        $partner = TradePartner::findOne(['name' => static::SCOPE_SHOP_REFUND]);
        if (!$partner) {
            return $this->addError($attribute, '非授权的收款应用方式');
        }
        $this->_partner = $partner;
    }

    /**
     * @return TradePartner|null
     */
    protected function getPartner() {
        return $this->_partner;
    }

    /**
     * 主线流程
     */
    public function refund() {
        if (!$this->validate()) {
            Yii::error($this->getErrorMsg());
            return false;
        }
        //调起退款
        $refund = $this->refund;
        /* @var $unifiedOrder ShopOrderUnified */
        $unifiedOrder = $refund->unifiedOrder;
        /* @var $order ShopOrder */
        $order = $refund->order;
        /* @var $refundTrade Trade */
        $refundTrade = $refund->refundTrade;
        if (!$refundTrade) {
            Yii::error([
                '未找到退款交易，准备创建新退款交易'
            ]);
            $refundTrade = $this->createRefundTrade($refund, $order, $unifiedOrder);
        }
        if (!$refundTrade) {
            Yii::error([
                '未找到退款交易，准备创建新退款交易'
            ]);
            return false;
        }
        if ($refundTrade->status == Trade::STATUS_DEFAULT) {
            $refundTrade->status = Trade::STATUS_WAITPAY;
        }
        if (!$refundTrade->save()) {
            Yii::error([
                '退款交易状态更新失败，需要重试'
            ]);
            return false;
        }
        $refundOrders = $refundTrade->orders;
        foreach ($refundOrders as $refundOrder) {
            if (!$refundOrder->canRefund) {
                Yii::error([
                    '交易订单不能退款，继续寻找下一个'
                ]);
                continue;
            }
            if ($refundOrder->paymode == TradeOrder::PAYMODE_ALIPAY) {
                Yii::error([
                    '交易订单-退款-支付宝'
                ]);
                $this->alipayRefund($refundTrade, $refund->order_trade_no);
            } elseif ($refundOrder->paymode == TradeOrder::PAYMODE_WECHAT) {
                Yii::error([
                    '交易订单-退款-微信'
                ]);
                $orderPaymentTradeOrder = $refund->paymentCashOrder;
                if ($orderPaymentTradeOrder) {
                    $this->wechatRefund($refundTrade, $refund->order_trade_no, $orderPaymentTradeOrder->pay);
                }
            } elseif ($refundOrder->paymode == TradeOrder::PAYMODE_CREDIT) {
                Yii::error([
                    '交易订单-退款-积分'
                ]);
                $account = $refundTrade->userAccount;
                $this->creditRefund($refundTrade->creditOrder, $account, '商品退款');
            }
        }
        //再次查询确认
        $refundAmount = $refundTrade->getOrders()->sum('pay*status*balance');
        $refundTradeAmount = $refundTrade->total * $refundTrade->balance;
        $same = MathHelper::bccomp($refundAmount, $refundTradeAmount);
        if ($same !== 0) {
            Yii::error([
                'message' => '交易订单-退款-金额不一致，等待退款结果',
                '$refundAmount' => $refundAmount,
                '$refundTradeAmount' => $refundTradeAmount,
                '$same' => $same,
            ]);
            //等待退款结果
            return;
        }
        $refundTrade->status = Trade::STATUS_SUCCESS;
        if ($refundTrade->save()) {
            //通知业务订单成功了
            $notify = new BusinessNotify($refundTrade->id);
            $notifyStatus = $notify->save($msg);
            if ($notifyStatus) {
                Yii::error([
                    '交易订单-退款-成功，已通知业务订单处理'
                ]);
                return;
            }
            Yii::error($msg, 'payment');
        }
        if (abs($refundAmount) == $refund->refund_pay) {
            Yii::error([
                '交易订单-退款-成功，准备执行下一步操作'
            ]);
            //执行下一流程
            return $refund->nextWorkflow();
        }
        Yii::error([
            '交易订单-退款-结果未知，等待结果处理',
            'refundAmount' => $refundAmount,
            'refund_pay' => $refund->refund_pay,
        ]);
        return;
    }

    /**
     * 账户积分退款
     * @param TradeOrder $refundOrder
     * @param UserAccount $account
     * @param string $prefixMessage
     * @return void
     * @throws Exception
     */
    protected function creditRefund($refundOrder, $account, $prefixMessage) {
        if (!$account) {
            return;
        }
        if ($refundOrder->status == TradeOrder::STATUS_SUCCESS) {
            return;
        }
        $transaction = Yii::$app->db->beginTransaction();
        try {
            $points = $refundOrder->pay * 10;
            //回滚账户积分
            $account->setPoints($points, UserAccount::USEAGE_SHOP_REFUND);
            if (!$account->save()) {
                throw new Exception('账户积分回滚失败！');
            }
            $trade = $refundOrder->trade;
            $mark = '[' . $prefixMessage . ']系统自动退回订单支付积分:' . $trade->subject;
            $creditLog = $account->recordLog(UserAccountLog::CHANGE_TYPE_SHOP_REFUND, $points, 'trade_order', $refundOrder->id, $mark);
            //回滚成功修改退还订单
            $refundOrder->pay_time = time();
            $refundOrder->status = TradeOrder::STATUS_SUCCESS;
            $refundOrder->pay_no = (string) $creditLog->id;
            if (!$refundOrder->save()) {
                throw new Exception('退款积分通知保存时失败！');
            }
            $transaction->commit();
        } catch (Exception $e) {
            $transaction->rollBack();
            $msg = $e->getMessage();
            Yii::error($msg, __METHOD__);
            return false;
        } catch (Throwable $t) {
            $transaction->rollBack();
            $msg = $t->getMessage();
            Yii::error($msg, __METHOD__);
            return false;
        }
        return true;
    }

    /**
     * 
     * @param Trade $trade
     * @return string
     */
    protected function alipayRefund($trade, $outTradeNo) {
        if (!$this->partner) {
            return '';
        }
        if (!YII_ENV_PROD || YII_DEBUG) {
            return;
        }
        try {
            $requestName = TradeRequest::REQUEST_REFUND;
            $request = new TradeRequest($requestName);
            $bizContent = [
                'out_trade_no' => $outTradeNo,
                'out_request_no' => $trade->trade_no,
                'refund_amount' => $trade->cash,
                'refund_reason' => $trade->body,
//            'notify_url' => $trade->getNotifyUrl(),
//            'time_expire' => date('Y-m-d H:i', $trade->expire_time),
            ];
            Yii::error($bizContent, __METHOD__);
            $request->setBizContent($bizContent);
            /* @var $alipay AopClient */
            $alipay = Yii::$app->alipay->transport($this->partner->account_id);
            $response = $alipay->execute($request);
            $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
            $resultCode = $response->$responseNode->code;
            if (!empty($resultCode) && $resultCode == 10000) {
                return true;
            } else {
                return false;
            }
        } catch (Exception $e) {
            $msg = $e->getMessage();
            Yii::error($msg, __METHOD__);
            return false;
        } catch (Throwable $t) {
            $msg = $t->getMessage();
            Yii::error($msg, __METHOD__);
            return false;
        }
    }

    /**
     * 获取调起支付时微信所需的参数
     */
    protected function wechatRefund($trade, $outTradeNo, $total) {
        if (!YII_ENV_PROD || YII_DEBUG) {
            return;
        }
        $attributes = [
            'out_trade_no' => $outTradeNo,
            'out_refund_no' => $trade->trade_no,
            'total_fee' => MathHelper::bcmul($total, 100),
            'refund_fee' => MathHelper::bcmul($trade->cash, 100),
            'refund_desc' => $trade->body,
        ];
        try {
            /* @var $wechatPay PaymentWechat */
            $wechatPay = Yii::$app->wxpay;
            return $wechatPay->refund($attributes);
        } catch (Exception $e) {
            $msg = $e->getMessage();
            Yii::error($msg, __METHOD__);
            return false;
        } catch (Throwable $t) {
            $msg = $t->getMessage();
            Yii::error($msg, __METHOD__);
            return false;
        }
    }

    /**
     * 创建退款交易
     * @param ShopOrderRefund $refund
     * @param ShopOrder $order
     * @param ShopOrderUnified $unifiedOrder
     * @return boolean
     * @throws Exception
     */
    protected function createRefundTrade($refund, $order, $unifiedOrder) {
        /* @var $paymentTrade Trade */
        $paymentTrade = $refund->paymentTrade;
        //
        $refundCreditFees = intval($order->pay_credit * 10);
        $refundCashFees = intval($order->pay * 100 - $refundCreditFees);
        $cash = 0;
        $credit = 0;
        if ($refund->amount !== $order->pay) {
            //部分退款(按商品退，商品多件不支持拆分)
            $refundGoods = $refund->refundGoods;
            //积分退款金额
            $refundCreditFees = 0;
            foreach ($refundGoods as $_goods) {
                $refundCreditFees += intval($_goods->pay_credit * 10);
            }
            //现金退款金额
            $refundCashFees = intval($refund->amount * 100 - $refundCreditFees);
        }
        if ($this->_canRefundCashFees > 0 && $this->_canRefundCashFees >= $refundCashFees) {
            //现金退款金额
            $cash = round($refundCashFees / 100, 2);
        }
        if ($this->_canRefundCreditFees > 0 && $this->_canRefundCreditFees >= $refundCreditFees) {
            //积分退款金额
            $credit = round($refundCreditFees / 100, 2);
        }
        $cashTradeOrder = null;
        if ($cash > 0) {
            $cashTradeOrder = new TradeOrder();
            $cashTradeOrder->setAttributes([
                'paymode' => $paymentTrade->paymode,
                'pay_type' => '',
                'currency' => TradeOrder::CURRENCY_CNY,
                'pay' => $cash,
                'balance' => TradeOrder::BALANCE_REFUND,
                'status' => TradeOrder::STATUS_DEFAULT,
                'mark' => '现金交易退款',
            ]);
        }
        $creditTradeOrder = null;
        if ($credit > 0) {
            //创建积分退款
            $creditTradeOrder = new TradeOrder();
            $creditTradeOrder->setAttributes([
                'paymode' => TradeOrder::PAYMODE_CREDIT,
                'pay_type' => '',
                'currency' => TradeOrder::CURRENCY_CREDIT,
                'pay' => $credit,
                'balance' => TradeOrder::BALANCE_REFUND,
                'status' => TradeOrder::STATUS_DEFAULT,
                'mark' => '积分交易退款',
            ]);
        }
        Yii::error([
            '退款交易，准备创建新退款交易',
            '$credit' => $credit,
            '$cash' => $cash,
            '$refundCreditFees' => $refundCreditFees,
            '$refundCashFees' => $refundCashFees,
            '$paymentTrade' => $paymentTrade->attributes,
        ]);
        if (!$cashTradeOrder && !$creditTradeOrder) {
            Yii::error([
                '退款交易，都为空交易订单是退出',
            ]);
            return false;
        }
        $refundAmount = $credit + $cash;

        $transaction = Yii::$app->db->beginTransaction();
        try {
            $refundTrade = new Trade();
            $refundTrade->setAttributes([
                'user_id' => $refund->user_id,
                'paymode' => $paymentTrade->paymode,
                'total' => $refundAmount,
                'cash' => $cash,
                'credit' => $credit,
                'balance' => Trade::BALANCE_REDUCE,
                'status' => Trade::STATUS_DEFAULT,
                'platform' => 'SYSTEM',
                'partner_id' => 2,
                'unified_no' => $unifiedOrder->order_no,
                'bizno' => $refund->refund_no,
                'subject' => '商品退款',
                'body' => $refund->reason,
                'expire_time' => 0,
                'notified_at' => 0,
                'mark' => '商品退款来源：服务',
            ]);
            $refundTrade->generateTn($paymentTrade->paymode, 1, $credit);
            Yii::error([
                '退款交易:',
                $refundTrade->attributes
            ]);
            if (!$refundTrade->save()) {
                Yii::error([
                    '退款交易保存失败:',
                    $refundTrade->attributes
                ]);
                throw new Exception($refundTrade->getErrorMsg());
            }
            if ($cashTradeOrder) {
                $cashTradeOrder->trade_id = $refundTrade->id;
                $cashTradeOrder->original_no = $refundTrade->trade_no;
                Yii::error([
                    '创建现金交易退款订单:',
                    $cashTradeOrder->attributes
                ]);
                if (!$cashTradeOrder->save()) {
                    throw new Exception($cashTradeOrder->getErrorMsg());
                }
            }
            if ($creditTradeOrder) {
                $creditTradeOrder->trade_id = $refundTrade->id;
                $creditTradeOrder->original_no = $refundTrade->trade_no;
                Yii::error([
                    '创建积分交易退款订单:',
                    $creditTradeOrder->attributes
                ]);
                if (!$creditTradeOrder->save()) {
                    throw new Exception($creditTradeOrder->getErrorMsg());
                }
            }

            $refundTrade->recordLog('创建商品退款交易', $refund->reason);
            Yii::error([
                '记录退款交易日志:',
                $refundTrade->attributes
            ]);
            $refund->trade_id = $refundTrade->id;
            $refund->out_trade_no = $refundTrade->trade_no;
            $refund->refund_pay = $refundTrade->total;
            $refund->refund_pay_credit = floor($refundTrade->credit * 10);
            $refund->status = ShopOrderRefund::STATUS_REFUND;

            if (!$refund->save()) {
                throw new Exception($refund->getErrorMsg());
            }

            Yii::error([
                '退款信息更新:',
                $refund->attributes
            ]);

            $log = new ShopOrderRefundLog();
            $log->setAttributes([
                'order_id' => $refund->order_id,
                'refund_id' => $refund->id,
                'user_id' => $refund->user_id,
                'refund_status' => $refund->status,
                'data' => '系统自动退款',
                'workflow_id' => 0,
                'status' => ShopOrderRefundLog::STATUS_ACTIVE,
            ]);
            if (!$log->save()) {
                throw new Exception($log->getErrorMsg());
            }

            Yii::error([
                '记录退款日志:',
                $refund->attributes
            ]);

            $transaction->commit();
        } catch (Exception $e) {
            $transaction->rollBack();
            $msg = $e->getMessage();
            Yii::error($msg);
            return false;
        }
        return $refundTrade;
    }

}
